/*
 * RecvMediaPort.cpp
 *
 *  Created on: 2017年4月14日
 *      Author: chuanjiang.zh
 */

#include "RecvMediaPort.h"
#include "TFileUtil.h"
#include "Path.h"
#include "Utility.h"
#include "BasicMacro.h"
#include "TStringUtil.h"
#include "RtpConst.h"
#include "CLog.h"


static const unsigned char s_IMKH[] = {
	0x49, 0x4D, 0x4B, 0x48, 0x02, 0x01, 0x00, 0x00,
	0x02, 0x00, 0x00, 0x01, 0x10, 0x71, 0x01, 0x10,
	0x40, 0x1F, 0x00, 0x00, 0x80, 0x3E, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};


using namespace jrtplib;


static void MyRtpSinkProc(HANDLE handle, StreamPacket* pPacket, void* pContext)
{
	RecvMediaPort* pthis = (RecvMediaPort*)pContext;
	pthis->handlePacket(pPacket);
}


RecvMediaPort::RecvMediaPort():
	m_rtpSink(),
	m_originSdp(),
	m_connected(false),
	m_medium(),
	m_running(false),
	m_localPort(),
	m_pktCount()
{
}

RecvMediaPort::~RecvMediaPort()
{
}

void RecvMediaPort::setOriginSdp(const std::string& sdp)
{
	m_originSdp = sdp;
}

std::string RecvMediaPort::getLocalSdp()
{
	if (!m_connected)
	{
		m_localSdp = m_originSdp;
	}
	return m_localSdp;
}

int RecvMediaPort::setPeerSdp(std::string& peerSdp)
{
	m_connected = true;

	m_peerSdp = peerSdp;

	m_localSdp = apply(m_localSdp, m_peerSdp);

	int rc = setup(m_localSdp, m_peerSdp);

	return rc;
}

std::string RecvMediaPort::getPeerSdp(std::string& offerSdp)
{
	m_peerSdp = offerSdp;
	m_localSdp = apply(m_originSdp, offerSdp);
	int rc = setup(m_localSdp, offerSdp);
	if (rc != 0)
	{
		return std::string();
	}

	m_connected = true;

	return m_localSdp;
}

int RecvMediaPort::start()
{
	m_rtpSink.setProc(MyRtpSinkProc, this);

	m_rtpSink.open(m_localPort, m_medium.ip.c_str(), m_medium.port);

	if (m_dumpPath.size() > 0)
	{
		comn::FileUtil::write(s_IMKH, sizeof(s_IMKH), m_dumpPath.c_str(), false);
	}

	m_rtpSink.startRecord(m_dumpPath.c_str());

	//std::string cmdline = comn::StringUtil::format(m_playerPath.c_str(), m_dumpPath.c_str());
	//m_player.open(cmdline.c_str());

	m_running = true;

	return 0;
}

void RecvMediaPort::stop()
{
	m_running = false;

	m_rtpSink.stop();

	m_rtpSink.stopRecord();
}

void RecvMediaPort::setPath(const std::string& player, const std::string& dumpfile)
{
	m_playerPath = player;
	m_dumpPath = dumpfile;
}


std::string RecvMediaPort::apply(const std::string& localSdp, const std::string& peerSdp)
{
	sdp::SessionDescription local;
	local.parse(localSdp);

	sdp::SessionDescription peer;
	peer.parse(peerSdp);

	sdp::SessionDescription sdp;
	sdp.version = local.version;
	sdp.origin = local.origin;
	sdp.name = local.name;
	sdp.info = local.info;
	sdp.uri = local.uri;
	sdp.email = local.email;
	sdp.phone = local.phone;
	sdp.connection = local.connection;
	sdp.bandwidth = local.bandwidth;
	sdp.time = local.time;
	sdp.repeatTime = local.repeatTime;
	sdp.timeZone = local.timeZone;
	sdp.key = local.key;
	sdp.attributes = local.attributes;

	for (size_t i = 0; i < local.getMediumCount(); ++i)
	{
		sdp::Medium& medium = local.getMedium(i);
		size_t idx = peer.findMedium(medium.name);
		if (idx == (size_t)-1)
		{
			continue;
		}

		sdp::Medium& peerMedium = peer.getMedium(idx);

		sdp::Medium newMedium(medium.name, medium.port, medium.proto);
		newMedium.attributes = medium.attributes;
		newMedium.connection = medium.connection;
		newMedium.bandwidth = medium.bandwidth;
		newMedium.control = medium.control;

		for (size_t j = 0; j < medium.codecs.size(); ++j)
		{
			sdp::Codec& codec = medium.codecs[j];
			idx = peerMedium.findCodec(codec.name);
			if (idx == (size_t)-1)
			{
				continue;
			}

			sdp::Codec newCodec(codec);
			newCodec.payload = peerMedium.codecs[idx].payload;
			newMedium.addCodec(newCodec);
		}

		sdp.addMedium(newMedium);
	}

	return sdp.toString();
}

int RecvMediaPort::setup(const std::string& localSdp, const std::string& peerSdp)
{
	sdp::SessionDescription local;
	local.parse(localSdp);

	sdp::SessionDescription peer;
	peer.parse(peerSdp);

	setupMedium(local, peer, "video", m_medium, m_localPort);

	//comn::FileUtil::dump(local.toString(), "local.sdp");

	return 0;
}

bool RecvMediaPort::setupMedium(sdp::SessionDescription& local, sdp::SessionDescription& peer,
	const char* name, RtpMedium& rtpMedium, int& localPort)
{
	bool found = false;
	size_t localIdx = local.findMedium(name);
	size_t peerIdx = peer.findMedium(name);
	if ((localIdx != -1) && (peerIdx != -1))
	{
		sdp::Medium& localMedium = local.getMedium(localIdx);
		sdp::Medium& peerMedium = peer.getMedium(peerIdx);

		rtpMedium.ip = peer.getConnection(peerMedium).address;
		rtpMedium.port = peerMedium.port;
		localPort = localMedium.port;
		rtpMedium.mode = localMedium.getMode();
		

		if (!localMedium.codecs.empty())
		{
			sdp::Codec& localCodec = localMedium.codecs[0];
			rtpMedium.payload = localCodec.payload;

			found = true;
		}

	}
	return found;
}


std::string RecvMediaPort::getName()
{
	return "RecvMediaPort";
}

void RecvMediaPort::handlePacket(StreamPacket* pPacket)
{
	m_pktCount ++;

	if (m_pktCount % 300 == 0)
	{
		if (!m_player.isRunning())
		{
			m_player.close();

			std::string cmdline = comn::StringUtil::format(m_playerPath.c_str(), m_dumpPath.c_str());
			m_player.open(cmdline.c_str());
		}
	}
}
